package mylab.io.base.sync;

import com.google.common.base.Preconditions;
import com.google.common.eventbus.Subscribe;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import lombok.Getter;
import mylab.base.foundation.event.AsyncEventSubscriber;
import mylab.io.base.event.TerminalConnectionChangedEvent;
import mylab.io.base.model.Msg;
import mylab.io.base.model.Terminal;
import mylab.utils.common.guava.LocalCacheService;


/**
 * Couple缓存，为单个终端缓存配对消息
 */
@Getter
public class CoupleCache extends LocalCacheService<String, Couple> implements AsyncEventSubscriber {

    //单终端默认瞬间最大10K同步请求
    static final long MAX_SIZE = 1024 * 10;

    private final Terminal terminal;

    public CoupleCache(Terminal terminal) {
        super(MAX_SIZE);
        this.terminal = terminal;
        AsyncEventSubscriber.super.registerEventListener();
    }

    public void cacheRequest(Msg request) {
        Couple bean = new Couple(request);
        put(bean.getCode(), bean);
    }

    /**
     * 在单终端缓存中尝试匹配同步应答，并返回对应的请求msg
     */
    public Msg tryMatch(Msg response, String requestMsgCode) {
        for (Couple bean : getMap().values()) {
            Msg request = bean.getRequest();
            if (Objects.equals(request.getCode(), requestMsgCode)) {
                bean.getResponseFuture().set(response);
                return request;
            }
        }
        return null;
    }

    public Msg getResponse(Msg request, long timeout, TimeUnit unit) throws Exception {
        Preconditions.checkState(containsKey(request.getCode()), "未缓存此请求:" + request);

        Couple bean = get(request.getCode());
        Msg response = response = bean.getResponse(timeout, unit);
        remove(request.getCode());

        return response;
    }

    public void cleanCache(Msg request) {
        if (containsKey(request.getCode())) {
            remove(request.getCode());
        }
    }


    @Subscribe
    public void onTerminalConnectionChangedEvent(TerminalConnectionChangedEvent event) {
        boolean isConnected = event.getContent();
        if (!isConnected && Objects.equals(terminal, event.getSource())) {
            clear();
        }
    }

}
